Context & Lexical Environments

자바스크립트를 이해하는데 있어 중요한 기본개념 3가지

Syntax parsers

작성한 코드를 컴퓨터가 이해 가능한 언어로 번역, 문법을 확인해주는 프로그램

Lexical environments

프로그래밍 언어에서 lexical 이란 특정 단어나 문법과 연관이 있다는 의미로, 코드를 어느 위치 에 작성하느냐, 그 코드를 어떤것이 감싸고 있느냐 에 따라 전혀 다르게 작동한다는 의미

예를들어 위의 사진에서 변수 a는 hello 라는 함수 안에 위치하고 있기 때문에 hello() 내에서만 존재, 접근가능

Execution context

lexical environments 중에서 어떤 환경을 선택해서 실행되는지를 관리하는 Wrapper 이다.

Excution context (Global)

자바스크립트 코드를 실행하면

  1. Execution context 생성
  2. Global object(window), this 생성
  3. Outer environment 생성
  4. 작성한 코드 실행

순으로 진행된다. 자바스크립트에서 말하는 Global(전역)은 Not in a function, 특정 객체나 함수가 아님 을 의미한다 즉, 특정 객채(함수) 안에 (lexical evironmental) 존재하지 않는다는 의미


Hoisting

1
2
3
4
5
6
7
8
9
10
11
(1)
var a = 'Hello World';
function b() {
console.log('Called b');
}

b();
console.log(a)

// Called b
// Hello World

(1) 코드 실행결과를 예측하는 것은 쉬운 일이다.

1
2
3
4
5
6
7
8
9
10
11
12
(2)
b();
console.log(a)

var a = 'Hello World';

function b() {
console.log('Called b');
}

//called b
// undefined

하지만 (2)는? 변수 a 는 undefined, 함수 b는 정상적으로 출력이 됐다.

이러한 현상을 호이스팅(Hoisting)이라고 한다. 몇몇 설명에서는 마치 코드를 물리적으로 최상단으로 이동시킨 다는 식인데 이는 오해의 소지가 있다.

excution context는 두 단계가 있다 Creation phaseExcution phase

Creation phase

Execution context 가 생성되면 동시에 global object, this, outer environment를 생성하고 변수와 함수 저장을 위해 메모리공간을 먼저 할당한다. 이때 모든 변수는 먼저 undefined 로 할당되고 함수는 의도대로 할당되는데 이 과정을 Hoisting 이라고 한다.

Execution phase

이 모든 작업 이후 코드가 한줄 한줄 순서대로 실행 되면서 의도한 값들이 해당 변수에 할당 됨.


Function invokation

자바스크립트에서 함수를 호출하면 어떤 일이 벌어질까?

1
2
3
4
5
6
7
8
function b() {
}

function a() {
b();
}

a();
  1. Global Execution Context , Global Object, this 생성

  2. 메모리에 함수 a 와 b 를 위한 공간 셋업

  3. 한줄 한줄 실행하다 함수호출 a() 를 만나면

  4. a() 를 위한 새로운 Execution context 를 생성후 Execution Stack 에 삽입

  5. a() 함수 내부 로직을 실행한다 (in Execution context of a() )

  6. 그러다 새로운 함수 호출 b() 를 만나면, b()를 위한 새로운 Execution contex를 생성, 같은 과정 반복

  7. 각 함수 실행이 끝나는 순서대로 Execution stack 에서 빼내는 순으로 작동이 종료된다

즉, 함수가 호출되어 실행 될 때마다 새로운 execution context가 생성되고, Execution stack 에 삽입, 함수 내부로직을 실행한 뒤에는 Execution stack 에서 빠지고 해당 Execution context 도 사라진다. 그 다음 해당 함수 호출 이후의 코드들이 순차적으로 실행되는 식으로 작동한다.

다음 코드의 실행순서를 살펴보면,

  1. a() 호출 (컨텍스트 생성, 스택 삽입)
  2. b() 호출 (컨텍스트 생성, 스택 삽입)
  3. var d
  4. b() out (컨텍스트 삭제, 스택에서 out)
  5. var c
  6. a() out (컨텍스트 삭제, 스택에서 out)
  7. var d

순으로 진행 될 것이다.


Variable Environment

각 함수가 호출될때마다 새로운 Execution context 가 추가되고, 변수 myVar는 각각의 Execution context 내에 독립적으로 존재, 각기 다른 메모리공간에 할당된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function b() {
var myVar;
console.log(myVar);
}
function a() {
var myVar = 2;
console.log(myVar);
b();
}

var myVar = 1;
console.log(myVar);
a();
console.log(myVar);

//1
//2
//undefined
//1

변수는 결국 어떤 Execution context 내의 variable environment 속해있는가 에 따라 결정되고 이는 곧 스코프(scope) 개념으로 연결된다.


Scope Chain

함수가 호출될 때 자바스크립트 엔진은 해당 함수가 ‘’어디” 에 작성됐는지를 기반으로 해당 Execution context의 outer environment 에 대한 reference 를 생성한다. 자기가 속한 Execution context 에서 해당 변수를 찾지 못했다면, outer environment reference 가 가르키는 Execution context 에서 다시 해당 변수를 검색하고.. 그 끝은 결국 전역, Globla Execution context 까지 이른다.

1
2
3
4
5
6
7
8
9
10
11
12
function b() {
console.log(myVar);
}
function a() {
var myVar = 2;
b();
}

var myVar = 1;
a();

//1
  1. b() Execution context 에는 변수 myVar 가 없다.

  2. 따라서 outer environment 인 Global Execution context 에서 myVar=1 확인

  3. console.log(myVar) ==> 1

1
2
3
4
5
6
7
8
9
10
11

function a() {
function b() {
console.log(myVar);
}
var myVar = 2;
b();
}

var myVar = 1;
a();

위의 코드의 경우 Outer environment 는 b() ==> a() ==> Global execution 순으로 Scope chain 이 결정 될 것이다.

따라서 해당 함수가 어느 Execution context에 정의 되었는지 확인하면 (코드 내 어디에서 선언되었는지) scope chain을 확인 할 수있다.


Asynchronous Callback

자바스크립트는 동기식 (한번에 하나의 작업처리 순서대로).

동시에 여러개 처리 못함, 자바스크립트는 코드 한줄 한줄 순서대로 실행!

자바스크립트 엔진에는 Execution stack 외에 여러 이벤트들을 담는 Event queue 가 존재한다.

자바스크립트 엔진은 Event queue 를 계속 주시하고 있고, Execution stack이 빈상태가 됐을 때 (순차적으로 실행할 함수들이 다 실행되고 나면) , queue에 있는 이벤트가 발동되면 해당 이벤트의 콜백함수 ( ex) click , clickHandler() ) 를 Execution stack 으로 삽입시켜 함수를 실행하게 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 3초가 지나면 종료되는 함수
function waitThreeSeconds() {
var ms = 3000 + new Date().getTime();
while (new Date() < ms) {}
console.log('finished function');
}
function clickHandler() {
console.log('click event!');
}

// 웹페이지 아무대나 클릭하면 발동되도록 리스너 추가
document.addEventListener('click', clickHandler);

waitThreeSeconds();
console.log('finished execution');

//3초 이전에 클릭이벤트를 발생시켜도 결과는 아래와 같다
//finished function
//finished execution
//click event!

위의 코드실행 결과가 왜 저 순서대로 나오는지 이해가 되는가?

waitThreeSeconds() 는 3초가 지나면 실행을 종료한다 ( Execution context 사라짐) -> 한줄한줄 코드를 실행하다 끝에 다다른다 (Global Execution context 사라짐) -> Execution stack 빔 -> 그때서야 event queue 확인 -> 해당 event handler() 실행

즉, 자바스크립트 엔진은 Execution stack 이 비워지기 전까지 event queue를 처리하지 않다가 스택이 비게되면(코드를 순차적으로 다 실행하고 나서야) event queue 내에 준비된 event를 처리한다.

이것이 자바스크립트가 동기식으로 비동기식 작업을 처리하는 방식이다.


Type & Operator

Main

Share